#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <ctype.h>
#include <signal.h>
#include "jobs.h"

extern char** environ;//声明环境变量列表

//内建execvp函数
int my_execvp(const char* file, char* const argv[])//指针不可改，内容可改
{
    //检查是否为绝对路径，若是，则直接调用execve
    if (strchr(file, '/'))
    {
        execve(file, argv, environ);
        return -1;//execve失败，-1是my_execvp的返回值，帮助用户判断是否成功
    }

    char* path_env = getenv("PATH");//获取环境变量
    if (path_env == NULL)
    {
        fprintf(stderr, "PATH not found\n");
        return -1;
    }

    char* path_copy = strdup(path_env);//防止strtok修改环境变量，复制内容到一块新内存
    char* dir = strtok(path_copy, ":");//得到PATH众多可能地址中的第一个

    while (dir != NULL)//依次排查，直到找到对的绝对路径
    {
        char full_path[1024];
        snprintf(full_path, sizeof(full_path), "%s/%s", dir, file);//拼接成想要的绝对路径
        if (access(full_path, X_OK) == 0)//检查路径是否存在且可执行
        {
            execve(full_path, argv, environ);//成功后旧进程会被系统清理，不必再free
            perror("path wrong");
        }
        dir = strtok(NULL, ":");//当前路径错误，继续得到下一个路径
    }

    fprintf(stderr, "Command not found: %s\n", file);//没有找到路径
    free(path_copy);//strdup底层调用malloc，记得释放！！！
    return -1;
}

void execute_command(const char* cmd,
                     int is_first, int is_last,
                     int prev_fd, int pipefd[2],
                     int is_background)//单独处理每个管道
{
    pid_t pid = fork();
    if (pid == 0)
    {
        //把上一个管道的输出重定向为当前管道的输入
        if (!is_first && prev_fd != -1)
        {
            dup2(prev_fd, STDIN_FILENO);
            close(prev_fd);
        }
        //把当前命令的标准输出重定向到当前管道写端
        if (!is_last) {
            close(pipefd[0]);
            dup2(pipefd[1], STDOUT_FILENO);
            close(pipefd[1]);
        }

        // 拆分参数（处理为字符串存入数组）
        char cmd_copy[1024];
        strncpy(cmd_copy, cmd, sizeof(cmd_copy));//cmd被const修饰无法修改，因此拷贝一份
        char *args[64];
        int j = 0;

        char *ptr = strtok(cmd_copy, " ");
        while (ptr && j < 64)//以空格为断点截断每一个子命令存入数组方便查找重定向标识符
        {
            args[j++] = ptr;
            ptr = strtok(NULL, " ");
        }
        args[j] = NULL;
       
        //单独处理每一个重定向
        char input_file[128] = {0};
        char output_file[128] = {0};
        int append_mode = 0;//用来标记是否为追加输入 
        
        for (int i = 0; args[i]; ++i)//拷贝文件名
        {
            if (strcmp(args[i], "<") == 0 && args[i+1])
            {
                strncpy(input_file, args[i+1], sizeof(input_file));//空间有盈余会自动填充为‘\0’
            }
            else if (strcmp(args[i], ">>") == 0 && args[i+1])
            {
                strncpy(output_file, args[i+1], sizeof(output_file));
                append_mode = 1;
            }
            else if (strcmp(args[i], ">") == 0 && args[i+1])
            {
                strncpy(output_file, args[i+1], sizeof(output_file));
                append_mode = 0;
            }//先判断’>>",再判断‘>",因为反过来，'>>'会被当成两个’>'处理
        }

        //执行重定向
        if (is_first && input_file[0] != '\0')//第一个管道，且命令有重定向工作
        {
            int fd = open(input_file, O_RDONLY);
            if (fd < 0)
            {
                perror("open input failed");
                exit(1);
            }
            dup2(fd, STDIN_FILENO);//将文件内容重定向为标准输入
            close(fd);
        }

        if (is_last && output_file[0] != '\0') 
        {
            int flags = O_WRONLY | O_CREAT | (append_mode ? O_APPEND : O_TRUNC);
            int fd = open(output_file, flags, 0644);
            if (fd < 0)
            {
                perror("open output failed");
                exit(1);
            }
            dup2(fd, STDOUT_FILENO);
            close(fd);
        }

        //构造去重定向符号后的数组供my_execvp使用
        char *pure_args[64];
        int k = 0;
        for (int i = 0; args[i] != NULL; ++i)
        {
            if ((strcmp(args[i], "<") == 0 || strcmp(args[i], ">") == 0 || strcmp(args[i], ">>") == 0) && args[i+1] != NULL)
            {
                ++i; // 跳过 filename
            }
            else
            {
                pure_args[k++] = args[i];
            }  
        }
        pure_args[k] = NULL;

        //执行用户命令
        my_execvp(pure_args[0], pure_args);
        perror("exec failed");
        exit(1);
    }

    if (pid > 0)
    {
        if (is_background)
        {
            printf("[Background PID %d] %s\n", pid, cmd);
            add_job(pid, cmd);

            //打印 Running: 信息（在父进程做）
            char cmd_copy[1024];
            strncpy(cmd_copy, cmd, sizeof(cmd_copy));
            char *args[64];
            int j = 0;
            char *ptr = strtok(cmd_copy, " ");
            while (ptr && j < 64)
            {
                args[j++] = ptr;
                ptr = strtok(NULL, " ");
            }
            args[j] = NULL;

            printf("Running: ");
            for (int m = 0; args[m]; ++m)
                printf("[%s] ", args[m]);
            printf("\n");
         }  
        else
        {
            waitpid(pid, NULL, 0);
        }
    }
}

void handle_sigchld(int sig)
{
    int status;
    pid_t pid;
    char buf[256];

    while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
    {
        //把字符串写入buf，最多写入 sizeof(buf) - 1 个字符，加上 \0 结尾
        int len = snprintf(buf, sizeof(buf), "[Background Process %d exited]\n", pid);
        write(STDOUT_FILENO, buf, len);//系统调用，无缓冲、不会自动加换行
        remove_job(pid);//回收成功，移除列表
    }
}

void run_shell()
{
    signal(SIGCHLD, handle_sigchld);//信号处理函数，捕获并调用用户提供的函数来处理信号
    char command[1024];

    while (1) {
        // 显示当前路径
        char cwd[1024];
        if (getcwd(cwd, sizeof(cwd)) != NULL) 
        {
            printf("[%s] myshell> ", cwd);
            fflush(stdout);
        }
        else
        {
            perror("getcwd failed");
        }

        if (fgets(command, sizeof(command), stdin) == NULL)
            continue;//若输入失败，跳出本次while循环

        // 处理末尾空格与换行
        int len = strlen(command);
        while (len > 0 && isspace(command[len - 1])) len--;
        command[len] = '\0';  // 去除尾部空格或换行

        // 检查是否为后台执行
        int is_background = 0;
        if (len > 0 && command[len - 1] == '&')
        {
            is_background = 1;
            command[len - 1] = '\0';
        }

        if (strcmp(command, "jobs") == 0)
        {
            print_jobs();  // 打印当前后台任务列表
            continue;      // 不再进入执行流程
        } 

        // exit 命令退出 shell
        if (strcmp(command, "exit") == 0)
            break;

        //内建cd
        char command_copy[1024];
        strncpy(command_copy, command, sizeof(command_copy));

        char* first_token = strtok(command_copy, " ");
        if (first_token && strcmp(first_token, "cd") == 0)
        {
            char* path = strtok(NULL, " ");
            if (!path)
            {
                fprintf(stderr, "cd: missing operand\n");
            } 
            else if (chdir(path) != 0)//chdir返回0代表执行成功 
            {
                perror("cd failed");
            }
            continue;  // 跳过执行阶段
        }  

        // 拆分管道
        char *cmds[10];
        int cmd_count = 0;
        char *token = strtok(command, "|");
        while (token && cmd_count < 10)
        {
            while (*token == ' ') token++;  // 去前导空格
            cmds[cmd_count++] = token;
            token = strtok(NULL, "|");
        }

        int prev_fd = -1;//下文表示上一个管道的读端
        int pipefd[2];

        for (int i = 0; i < cmd_count; ++i)
        {
            if (i < cmd_count - 1 && pipe(pipefd) < 0)
            {
                perror("pipe failed");
                exit(1);
            }

            execute_command(cmds[i], i == 0, i == cmd_count - 1,
                            prev_fd, pipefd, is_background);
            //处理父进程管道关闭
            if (prev_fd != -1) close(prev_fd);
            if (i < cmd_count - 1)
            {
                close(pipefd[1]);
                prev_fd = pipefd[0];
            }
        }
        fflush(stdout);
    }
}

